(function($) { ... }(jQuery)
We have seen in the previous chapter how to use GateIn module system and the script integration. The goal of this section is to provide a list of common use case and how they can be addressed by GateIn. The section is two parts: the module cookbook and the script cookbook.
Let's study how to declare a GateIn module, the integration of the Highlight.js library. This example can be found among the GateIn examples in the amd-js.war. The Highlight.js is actually a jQuery plugin, jQuery plugins are perfect examples as they are natural GateIn modules and follow the self-invoking pattern that consumes the jquery dependency as $. Here is an overview of the Highlight.js source code:
(function($) { ... }(jQuery)
It is integrated using the XML declaration:
<module> <name>highlight</name> <script> <path>/highlight/highlight.js</path> </script> <depends> <module>jquery</module> <as>jQuery</as> </depends> </module>
The module is named highlight and uses the /highlight/highligh.js source code bundled in the war file
The depends tag creates a dependency on the jquery module. The dependency is aliased as $ using the as tag to match the Highlight.js self-executing function argument $.
The jquery plugins comes out of the box with GateIn and one should not worry about declaring it. The jquery module is simply named jquery and provides the jQuery 1.7.1 .
In the previous section we mentioned that GateIn can integrate native AMD modules since GateIn modules are translated to AMD modules. Asynchronous module declaration is well explained in the RequireJS documentation and you should read it if you want to be familiar with the format.
AMD modules follow the pattern:
define("module", ["dependency1",...,"dependencyN"], function(dep1,...,depN) { });
GateIn can use such module out of the box, however some parts will be overridden by the XML declaration:
The "module" name will be ignored and replaced by the declared module name
The module dependencies from "dependency1" to "dependencyN" have to be declared with the same name in XML (we will see later that we can use the as tag to override the dependency name)
Assuming that the dependencies dependency1 to dependencyN have been declared in XML, such module definition can be declared with the following XML:
<module> <name>MyModule</name> ... <depends> <module>dependency1</module> </depends> ... <depends> <module>dependencyN</module> </depends> </module>
GateIn provides the jQuery library 1.7.1 as a jquery module, the configuration of this module can be found in the eXoResources.war file. To reuse this jQuery version one just has to declare a dependency over it:
<portlet> <name>RequireJSPortlet</name> <module> <depends> <module>jquery</module> </depends> </portlet>
The default jquery module alias is $ so if you are using it, it should be named $ in the self-executing function:
(function($) { ... })($);
If your library use a different name such as jQuery the XML as tag should be used:
<portlet> <name>RequireJSPortlet</name> <module> <depends> <module>jquery</module> <as>jQuery</as> </depends> </portlet>
With the following self-executing function:
(function($) { ... }(jQuery);
If you are not satisfied by the jQuery version provided by GateIn you can integrate the version you like. It is not uncommon that products built over GateIn depends on third party JavaScript frameworks depending on other versions of jQuery libraries, so deploying other jQuery libraries is unavoidable at some point. Having multiple jQuery instances within a web page is prone to conflict over global variables. The beauty of the module system is that you can use such library with no hassles.
Consider an example with a jQueryPortlet using jQuery version 1.6.4, what we need to do is to configure the two modules properly:
<module> <name>jquery-1.6.4</name> <script> <adapter> (function() { <include>/javascript/jquery-1.6.4.js</include> return jQuery.noConflict(true); })(); </adapter> </script> </module> <portlet> <name>jQueryPortlet</name> ... <depends> <module>jquery-1.6.4</module> </depends> </portlet>
In this section, we are going to see how to configure a jQuery plugin and how to use it in jQueryPluginPortlet portlet. The plugin code is a minimal plugin:
(function($) { $.fn.doesPluginWork = function() { alert('YES, it works!'); }; })(jQuery);
The plugin is then declared as a module:
<module> <name>jquery-plugin</name> <as>jqPlugin</as> <script> <path>/jqueryPlugin/jquery-plugin.js</path> </script> <depends> <module>jquery</module> <as>jQuery</as> </depends> </module>
Finally we use this plugin in our portlet:
<portlet> <name>jQueryPluginPortlet</name> <module> <script> <path>/jqueryPlugin/jqueryPluginPortlet.js</path> </script> <depends> <module>jquery</module> <as>$</as> </depends> <depends> <module>jquery-plugin</module> </depends> </module> </portlet>
One important point to have in mind is that our portlet module should depend on the jquery and the jquery-plugin modules even it will not use the plugin itself:
declaring the dependency on jquery allows to use the jQuery object
delcaring the dependency on jquery-plugin ensures that the plugin will be loaded in the jquery dependency before it is injected in the portlet module
We have seen previously how to declare a native AMD module and said that the module dependency names must match the AMD dependencies declared in the define function arguments. When there is a mismatch between a module declared in the native module and the module system of GateIn the as tag can be used for renaming the dependencies.
Let’s say that there is a foo.js file defining an AMD module named foo with two dependencies "dep1", "dep2" as following :
define("foo", ["dep1", "dep2"], function(a1, a2) { // The module });
Now let's suppose that the dependencies are declared as module1 and module2 in GateIn, as we can see the names don't match. To override this we can use the as tag to rename the dependencies:
<module> <name>foo</name> <script> <path>/path/to/foo.js</path> </script> <depends> <module>module1</module> <as>dep1</as> </depends> <depends> <module>module2</module> <as>dep2</as> </depends> </module>
CommonJS defines its own module format, although it is not supported natively by GateIn, the adapter format can be used to adapt CommonJS modules to work nicely in GateIn.
Here are two simple CommonJS modules:
exports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum; };
var add = require('math').add; exports.inc = function(val) { return add(val, 1); };
CommonJS modules use its require function which is conflict with RequireJS same function. So in order to make it works in AMD
enabled environment, these modules need to be wrapped and injected predefined modules: require, exports and module provided by Requirejs (details here). The good news is that developers don't need to do this themselves, GateIn will wrap the code for you based on configuration using the adapter format:
<module> <name>math</name> <script> <adapter> define(["require", "exports"], function(require, exports) { <include>/commonjs/math.js</include> }); </adapter> </script> <depends> <module>require</module> </depends> <depends> <module>exports</module> </depends> </module> <module> <name>increment</name> <script> <adapter> define(["require", "exports", "math"], function(require, exports) { <include>/commonjs/increment.js</include> }); </adapter> </script> <depends> <module>require</module> </depends> <depends> <module>exports</module> </depends> <depends> <module>math</module> </depends> </module>
Mustache.js is a popular JavaScript template engine. Mustache is written to be executed in several kind of environment : as a global object, as a CommonJS module or as a native AMD module. If "module", "exports" dependencies are available Mustache will register it as a CommonJS module. It can be adapted to GateIn thanks to the adapter format:
<module> <name>mustache</name> <script> <adapter> define(["require", "exports", "module"], function(require, exports, module) { <include>/requirejs/js/plugins/mustache.js</include> }); </adapter> </script> <depends> <module>require</module> </depends> <depends> <module>exports</module> </depends> <depends> <module>module</module> </depends> </module>
We need to use adapter tag here and declare the require, exports and module dependencies of the CommonJS module. Now any module can have Mustache instance injected just by declaring it in its dependencies list:
<module> <name>foo</name> ... <depends> <module>mustache</module> </depends> </module>
(function(mustache){ //code that use Mustache mustache.render(template); })(mustache);
RequireJS provides support for loader plugin allowing a module to be a plugin and use the AMD system for loading web resources in an efficient manner.
Mustache.js is a javascript template engine and the engine need a template file to parse and generate html. When there are many templates or the template has a large size, embedding template in the page is not a good choice for front end performance reason. It would be better to use Text.js to load the separate template files and inject them as dependencies.
Text.js is a native AMD module, it also depends on the module dependency predefined dependency provided by the AMD loader. Thanks to the native AMD support of GateIn, it is straight foward to declare and use Text.js in GateIn:
<module> <name>text</name> <script> <path>/requirejs/js/plugins/text.js</path> </script> <depends> <module>module</module> </depends> </module>
Now we can use the mustache and text modules to load templates and render them in our own module:
<portlet> <name>foo</name> <module> ... <depends> <module>mustache</module> </depends> <depends> <module>text</module> <as>tmpl</as> <resource>/path/to/template.html</resource> </depends> </module> </portlet>
We have the text module in dependency list with a <resource> tag, Text.js will load that resource template and inject it with the name tmpl. Here is the javascript of the portlet:
function(mustache, tmpl) { var html = mustache.render(tmpl); //append rendered html to DOM })(mustache, tmpl);
Sometimes it is required to access a module from a script, RequireJS provides such capability by using the require function to execute a function in the managed context:
require([“SHARED/ModuleA”], function(a) { // Codes of interacting with module A a.doSomething(); });
In such situation we need to use the AMD module name of the module we need to depend on, PORTLET/ModuleA in this case. The prefix in upper case is the module scope among SHARED, PORTLET and PORTAL.
As explained previously, the built-in jQuery is currently declared as an AMD module. By default jQuery will not be available in the window object of the browser. This cookbook recipe shows how to make it available so one can write code like in a plain script.
The following script will make jQuery available by mounting the jQuery object in the window object:
require( [“SHARED/jquery”], function($) { // the ‘$’ in window.$ is alias, you can make the other for yourself. window.$ = $; });
This script must be integrated as a shared script:
<scripts> <name>imediatejs</name> <script> <path>/myfolder/imediateJScript.js</path> </script> </scripts>
A portlet can then provide its own script that depends on this script:
<portlet> <name>foo</name> <script> <name>portletjs</name> <path>/myfolder/portlet.js</path> </script> <depends> <scripts>imediatejs</scripts> </depends> </scripts>
With the following JavaScript:
$("#foo").html("<h1>hello global jQuery</h1>");
We can define a globally available custom jQuery quite easily. For this we reuse existing recipes seen previously and combine them:
Use a custom jQuery version
Expose GateIn version of jQuery globally
There are a few ways to implement this recipe. However, the most important point to keep in mind is to make sure that the global jQuery is available before the global jQuery plugin is loaded.
We have seen before how we can scope a module to a portlet, the module will be loaded when the portlet is on a page using the PORTLET scope. In this recipe we will change and use instead a PORTAL scope, the main difference is that the loading of our plugin will be triggered on a specific portal instead of a specific portlet.
First step we create our jQuery plugin as a script named myPlugin.js and we integrate our plugin:
require([“SHARED/jquery”], function($) { $.fn.myPluginFunction = function() { // Your work here; }; });
Second step we bind the script in our portal and also reuse the immediatejs script seen before:
<portal> <name>classic</name> <scripts> <script> <name>myPlugin</name> <path>/myfolder/myPlugin.js</path> </script> <script> <name>imediatejs</name> <path>/myfolder/imediateJScript.js</path> </script> </scripts> </portal>
Now, our plugin is globally available and we can use it:
<script type=”text/javascript”> $(‘#foo’).myPluginFunction(); </script>